#pragma once 

#include<iostream>

#include<pthread.h>
#include<unistd.h>
#include<string>
#include<queue>

namespace BlockQueueModule
{
    template<class T>
    class BlockQueue
    {
        public:
        BlockQueue(const int cap)
        :_cap(cap),_num_consumer(0),_num_productor(5)
        {
            pthread_cond_init(&_consumer_cond,nullptr);
            pthread_cond_init(&_productor_cond,nullptr);
            pthread_mutex_init(&_mutex,nullptr);
        }
        bool isEmpty()
        {
            return _que.empty();
        }
        bool isFull()
        {
            return  _que.size()==_cap;
        }
        void Equeue(const T& in)//生产者
        {
            pthread_mutex_lock(&_mutex);
            //你想放资源就可以放吗
            //必须得是队列不是满的才可以放
            //放了之后一定是不是有数据
            //有数据是不是就可以唤醒消费者



            //在临界区等待是必然的
            //原因：锁也是临界资源
            while(isFull())// 对条件进行判断用while 防止伪唤醒
            {
                //临界资源满了 生产者不能生产了开始阻塞
                std::cout<<"生产者开始等待"<<std::endl;
                _num_productor++;
                pthread_cond_wait(&_productor_cond,&_mutex);
                //等待时候持有锁有问题吗，会造成死锁吗？
                //不会 该函数会让生产者在条件变量上等  把锁资源给释放了
                //那释放了锁资源会不会影响后续，毕竟后续还要释放一次
                //不会 因为条件变量允许了之后 还要申请锁
                _num_productor--;
                std::cout<<"生产者被开始唤醒"<<std::endl;
            }
            //运行到这一定是资源不满了 可以放资源了 
            _que.push(in);//一定有资源 只要有消费者等待就直接唤醒
            if(_num_consumer)
            {
                std::cout<<"消费者被唤醒"<<std::endl;
                pthread_cond_signal(&_consumer_cond);
            }

            pthread_mutex_unlock(&_mutex);
        }
        void Pop(T* out)
        {
            pthread_mutex_lock(&_mutex);

            //你想删数据就删数据吗
            //必须得是不是空的才能删
            //删除了之后一定是有了空间
            //有了空间就可以唤醒生产者
            while(isEmpty())
            {
                std::cout<<"消费者阻塞"<<std::endl;

                _num_consumer++;
                pthread_cond_wait(&_consumer_cond,&_mutex);
                _num_consumer--;

                std::cout<<"消费者被唤醒"<<std::endl;
            }

            //运行到这 一定是因为队列里不是空的 一定是因为有了数据
            *out=_que.front();
            _que.pop();
            //肯定有空间
            if(_num_productor)
            {
                pthread_cond_signal(&_productor_cond);
            }

            pthread_mutex_unlock(&_mutex);
        } 
        ~BlockQueue()
        {
            
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_consumer_cond);
            pthread_cond_destroy(&_productor_cond);
        }
        private:
        std::queue<T> _que;
        pthread_mutex_t _mutex;
        pthread_cond_t _consumer_cond;
        pthread_cond_t _productor_cond;
        int _cap;
        int _num_consumer;//等待的的消费者线程的数量
        int _num_productor;//等待的生产者线程的数量


    };

    //为什么有伪唤醒 如果用的是if(isfull())
    //那么我在某个时候突然发了好几次唤醒 或者 就是cond_bro 唤醒了好几个线程
    //但资源我只生产了一个 消费者不够用 怎么办
    //这个就是伪唤醒
    //如果用while 就不会伪唤醒 因为如果不够 就会让他在条件变量上等了


}